﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ContentMigrationHelper.cs" company="Collaboris Ltd.">
//   Copyright (c) Collaboris Ltd. All rights Reserved.
// </copyright>
// <summary>
//   Defines the ContentMigrationHelper type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Collaboris.SharePoint.Wss.ContentMigration
{
    using System;

    using Microsoft.SharePoint;
    using Microsoft.SharePoint.Deployment;

    using Collaboris.SharePoint.Wss.Resources;

    using Collaboris.Utils;

    using Webs;

    public class ContentMigrationHelper : WssHelperBase
    {
        #region Fields (2) 

        private SPList importList;
        private SPWeb importWeb;
        private bool deleteDestinationWeb;
        private bool makeInternalLinksRelative;

        #endregion Fields 

        #region Methods (8) 

        // Public Methods (7) 

        /// <summary>
        /// Exports a List to a CMP
        /// </summary>
        /// <param name="exportObjects">The export objects.</param>
        /// <param name="list">The list.</param>
        public void ExportList(SPExportObjectCollection exportObjects, SPList list)
        {
            this.Trace.TraceMethodStart("ExportList");
            ValidationHelper.VerifyObjectArgument(exportObjects, "exportObjects");
            ValidationHelper.VerifyObjectArgument(list, "list");

            foreach (SPListItem item in list.Items)
            {
                this.ExportListItem(exportObjects, item);
            }
            this.Trace.TraceMethodEnd("ExportList");
        }

        /// <summary>
        /// Exports a Lists (list Items), to a CMP
        /// </summary>
        /// <param name="exportSettings">The export settings.</param>
        /// <param name="list">The list.</param>
        /// <param name="srcWebUrl">The SRC web URL.</param>
        public void ExportList(ExportSettings exportSettings, SPList list, string srcWebUrl)
        {
            this.Trace.TraceMethodStart("ExportList(ExportSettings exportSettings, SPList list)");

            ValidationHelper.VerifyObjectArgument(exportSettings, "exportSettings");
            ValidationHelper.VerifyObjectArgument(exportSettings.FileLocation, "exportSettings.FileLocation");
            ValidationHelper.VerifyObjectArgument(exportSettings.BaseFileName, "exportSettings.BaseFileName");
            ValidationHelper.VerifyObjectArgument(list, "list");
            ValidationHelper.VerifyObjectArgument(srcWebUrl, "srcWebUrl");

            // configure the settings.
            SPExportSettings settings = new SPExportSettings();
            
            InitExportSettings(exportSettings, srcWebUrl, settings);

            // now lets actually go and add ALL of the List Items to the settings.
            this.ExportList(settings.ExportObjects, list);

            // Run the Export.
            SPExport export = new SPExport(settings);
            export.Run();

            this.Trace.TraceMethodEnd("ExportList(ExportSettings exportSettings, SPList list)");
        }

        /// <summary>
        /// Exports a List Item to a CMP
        /// </summary>
        /// <param name="exportObjects">The export objects.</param>
        /// <param name="listItem">The list item.</param>
        public void ExportListItem(SPExportObjectCollection exportObjects, SPListItem listItem)
        {
            this.Trace.TraceMethodStart("ExportListItem");
            
            ValidationHelper.VerifyObjectArgument(exportObjects, "exportObjects");
            ValidationHelper.VerifyObjectArgument(listItem, "listItem");
            
            SPExportObject exportObject = new SPExportObject();
            exportObject.Id = listItem.UniqueId;
            //exportObject.Url = listItem.Url;
            exportObject.Type = SPDeploymentObjectType.ListItem;
            exportObjects.Add(exportObject);
            this.Trace.TraceMethodEnd("ExportListItem");
        }

        /// <summary>
        /// Exports a List Item to a CMP
        /// </summary>
        /// <param name="exportSettings">The export settings.</param>
        /// <param name="listItem">The list item.</param>
        /// <param name="srcWebUrl">The SRC web URL.</param>
        public void ExportListItem(ExportSettings exportSettings, SPListItem listItem, string srcWebUrl)
        {
            this.Trace.TraceMethodStart("ExportListItem(ExportSettings exportSettings, SPListItem listItem)");

            ValidationHelper.VerifyObjectArgument(exportSettings, "exportSettings");
            ValidationHelper.VerifyObjectArgument(exportSettings.FileLocation, "exportSettings.FileLocation");
            ValidationHelper.VerifyObjectArgument(exportSettings.BaseFileName, "exportSettings.BaseFileName");
            ValidationHelper.VerifyObjectArgument(listItem, "listItem");
            ValidationHelper.VerifyObjectArgument(srcWebUrl, "srcWebUrl");

            // configure the settings.
            SPExportSettings settings = new SPExportSettings();

            InitExportSettings(exportSettings, srcWebUrl, settings);

            // now lets actuall go and add the List item to the settings.
            this.ExportListItem(settings.ExportObjects, listItem);

            // Run the Export.
            SPExport export = new SPExport(settings);
            export.Run();

            this.Trace.TraceMethodEnd("ExportListItem(ExportSettings exportSettings, SPListItem listItem)");
        }

        /// <summary>
        /// This will liip through all the lists in the Web
        /// and export the Lists (and in turn each ListItem)
        /// </summary>
        /// <param name="exportObjects">The export objects.</param>
        /// <param name="web">The web.</param>
        public void ExportWebListsOnly(SPExportObjectCollection exportObjects, SPWeb web)
        {
            this.Trace.TraceMethodStart("ExportWebListsOnly");
            
            ValidationHelper.VerifyObjectArgument(exportObjects, "exportObjects");
            ValidationHelper.VerifyObjectArgument(web, "web");

            foreach (SPList item in web.Lists)
            {
                this.ExportList(exportObjects, item);
            }
            this.Trace.TraceMethodEnd("ExportWebListsOnly");
        }

        /// <summary>
        /// Exports the Entire Web
        /// </summary>
        /// <param name="exportSettings">The export settings.</param>
        /// <param name="web">The web.</param>
        /// <param name="includeDesc">The include desc.</param>
        public void ExportWeb(ExportSettings exportSettings, SPWeb web, 
                              SPIncludeDescendants includeDesc)
        {
            this.Trace.TraceMethodStart("ExportWeb");

            ValidationHelper.VerifyObjectArgument(exportSettings, "exportObjects");
            ValidationHelper.VerifyObjectArgument(web, "web");

            // Create the Export Object
            SPExportObject exportObject = new SPExportObject();
            exportObject.Id = web.ID;
            exportObject.IncludeDescendants = includeDesc;
            exportObject.Type = SPDeploymentObjectType.Web;
            
            // Create the Settings
            SPExportSettings settings = new SPExportSettings();
            settings.SiteUrl = web.Url;
            settings.ExportMethod = SPExportMethodType.ExportAll;
            settings.FileLocation = exportSettings.FileLocation;
            settings.FileCompression = exportSettings.FileCompression;
            settings.BaseFileName = exportSettings.BaseFileName;
            settings.FileMaxSize = exportSettings.FileMaxSize;            
            settings.OverwriteExistingDataFile = exportSettings.OverwriteExistingDataFile;
            settings.LogFilePath = exportSettings.LogFilePath;
            settings.HaltOnWarning = true;
            settings.HaltOnNonfatalError = false;
            settings.ExcludeDependencies = ! exportSettings.IncludeDependencies;

            if (exportSettings.IncludeVersions)
                settings.IncludeVersions = SPIncludeVersions.All;
            else
                settings.IncludeVersions = SPIncludeVersions.CurrentVersion;

            if (exportSettings.IncludeSecurity)
                settings.IncludeSecurity = SPIncludeSecurity.All;
            else
                settings.IncludeSecurity = SPIncludeSecurity.None;

            settings.ExportObjects.Add(exportObject);

            // Run the Export.
            SPExport export = new SPExport(settings);
            export.Run();

            this.Trace.TraceMethodEnd("ExportWeb");
        }

        /// <summary>
        /// Exports a the lists contained in a Web Only
        /// </summary>
        /// <param name="exportSettings">The export settings.</param>
        /// <param name="web">The web.</param>
        public void ExportWebListsOnly(ExportSettings exportSettings, SPWeb web)
        {
            this.Trace.TraceMethodStart("ExportWebListsOnly(ExportSettings exportSettings, SPWeb web)");

            ValidationHelper.VerifyObjectArgument(exportSettings, "exportSettings");
            ValidationHelper.VerifyObjectArgument(exportSettings.FileLocation, "exportSettings.FileLocation");
            ValidationHelper.VerifyObjectArgument(exportSettings.BaseFileName, "exportSettings.BaseFileName");
            ValidationHelper.VerifyObjectArgument(web, "web");

            // configure the settings.
            SPExportSettings settings = new SPExportSettings();

            InitExportSettings(exportSettings, web.Url, settings);

            // now lets actually go and add ALL of the List Items to the settings.
            ExportWebListsOnly(settings.ExportObjects, web);

            // Run the Export.
            SPExport export = new SPExport(settings);
            export.Run();

            this.Trace.TraceMethodEnd("ExportWebListsOnly(ExportSettings exportSettings, SPWeb web)");
        }

        /// <summary>
        /// Imports a CMP containing list items to a given list
        /// </summary>
        /// <param name="importSettings">The import settings.</param>
        /// <param name="destinationList">The destination list.</param>
        /// <param name="destSiteUrl">The dest site URL.</param>
        /// <param name="destWebUrl">The dest web URL.</param>
        public void ImportToList(ImportSettings importSettings, SPList destinationList, string destSiteUrl, string destWebUrl) 
        {
            Trace.TraceMethodStart("ImportListItem");

            this.importList = destinationList;

            // set the import settings
            SPImportSettings settings = new SPImportSettings();
            InitImportSettings(importSettings, destSiteUrl, settings, destWebUrl);

            // Create the import object and set the import evgent handlers
            SPImport import = new SPImport(settings);
            EventHandler<SPDeploymentEventArgs> eventHandler = this.OnImportStarted;
            import.Started += eventHandler;

            EventHandler<SPObjectImportedEventArgs> importedEventHandler = this.OnImported;
            import.ObjectImported += importedEventHandler;

            this.Trace.TraceVerbose("Importing Content.");
            import.Run();

            Trace.TraceMethodEnd("ImportListItem");
        }

        /// <summary>
        /// Imports a CMP containing Web items to a given Web
        /// </summary>
        /// <param name="importSettings">The import settings.</param>
        /// <param name="destinationWeb">The destination web.</param>
        /// <param name="destSiteUrl">The dest site URL.</param>
        /// <param name="destWebUrl">The dest web URL.</param>
        /// <param name="deleteDestinationWebFirst">if set to <c>true</c> [delete destination web first].</param>
        public void ImportToWeb(ImportSettings importSettings, SPWeb destinationWeb, 
                                string destSiteUrl, string destWebUrl, bool deleteDestinationWebFirst)
        {
            Trace.TraceMethodStart("ImportWebItem");

            this.importWeb = destinationWeb;
            this.deleteDestinationWeb = deleteDestinationWebFirst;
            this.makeInternalLinksRelative = importSettings.MakeInternalLinksRelative;

            // set the import settings
            SPImportSettings settings = new SPImportSettings();
            InitImportSettings(importSettings, destSiteUrl, settings, destWebUrl);

            // Create the import object and set the import evgent handlers
            SPImport import = new SPImport(settings);
            EventHandler<SPDeploymentEventArgs> eventHandler = this.OnWebImportStarted;
            import.Started += eventHandler;

            EventHandler<SPObjectImportedEventArgs> importedEventHandler = this.OnImported;
            import.ObjectImported += importedEventHandler;

            this.Trace.TraceVerbose("Importing Content.");
            import.Run();

            Trace.TraceMethodEnd("ImportWebItem");
        }

        /// <summary>
        /// Configure the Import Settings
        /// </summary>
        /// <param name="importSettings"></param>
        /// <param name="destSiteUrl"></param>
        /// <param name="settings"></param>
        /// <param name="destWebUrl"></param>
        private static void InitImportSettings(ImportSettings importSettings, string destSiteUrl, 
                                               SPImportSettings settings, string destWebUrl)
        {
            settings.SiteUrl = destSiteUrl;
            settings.WebUrl = destWebUrl;
            settings.FileLocation = importSettings.FileLocation;
            settings.BaseFileName = importSettings.BaseFileName;
            settings.FileCompression = importSettings.FileCompression;
            settings.RetainObjectIdentity = importSettings.RetainObjectIdentity;
            settings.LogFilePath = importSettings.LogFilePath;
            settings.IgnoreWebParts = importSettings.IgnoreWebParts;
            
            if (importSettings.IncludeSecurity)
                settings.IncludeSecurity = SPIncludeSecurity.All;
            else
                settings.IncludeSecurity = SPIncludeSecurity.None;
        }
        // Private Methods (1) 

        /// <summary>
        /// Inits the export settings.
        /// </summary>
        /// <param name="exportSettings">The export settings.</param>
        /// <param name="webUrl">The web URL.</param>
        /// <param name="settings">The settings.</param>
        private static void InitExportSettings(ExportSettings exportSettings, string webUrl, SPExportSettings settings)
        {
            settings.SiteUrl = webUrl;
            settings.ExportMethod = SPExportMethodType.ExportAll;
            settings.FileLocation = exportSettings.FileLocation;
            settings.FileCompression = exportSettings.FileCompression;
            settings.BaseFileName = exportSettings.BaseFileName;
            settings.FileMaxSize = exportSettings.FileMaxSize;
            settings.ExcludeDependencies = true;
            settings.OverwriteExistingDataFile = exportSettings.OverwriteExistingDataFile;
            settings.LogFilePath = exportSettings.LogFilePath;
            settings.HaltOnWarning = true;
            settings.HaltOnNonfatalError = false;
            
            if (exportSettings.IncludeVersions)
                settings.IncludeVersions = SPIncludeVersions.All;
            else
                settings.IncludeVersions = SPIncludeVersions.CurrentVersion;

            if (exportSettings.IncludeSecurity)
                settings.IncludeSecurity = SPIncludeSecurity.All;
            else
                settings.IncludeSecurity = SPIncludeSecurity.None;
        }

        #endregion Methods 

        #region Event handlers

        
        private static string GetSourceListName(string sourceUrl)
        {
            int index = sourceUrl.LastIndexOf('/');
            return sourceUrl.Substring(index + 1);
        }

        private static string GetSourceWebName(string relativeListItemUrl)
        {
            int index = relativeListItemUrl.LastIndexOf('/');
            string valWithoutItem = relativeListItemUrl.Substring(0, index);

            index = valWithoutItem.LastIndexOf('/');
            return valWithoutItem.Substring(0, index);
        }

        /// <summary>
        /// Re-parents all list items to point to the destination list
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        private void OnImportStarted(object sender, SPDeploymentEventArgs args)
        {
            this.Trace.TraceMethodStart("ContentMigrationHelper.OnImportStarted");

            // If there is no destination list associated with this object then we need to throw an exception because we cannot procceed
            // because there is no way to select the object that we want to import. All objects on the exported file have to be imported
            // We need to make sure that all lists on the content file map to a corrrespondent destination  list.
            ValidationHelper.VerifyObjectArgument(this.importList, "importList");

            SPImportObjectCollection rootObjects = args.RootObjects;
            foreach (SPImportObject importObject in rootObjects)
            {
                if (importObject.Type == SPDeploymentObjectType.ListItem)
                {
                    // Reparent the list item
                    importObject.TargetParentUrl = this.importList.RootFolder.ServerRelativeUrl;
                }
            }

            this.Trace.TraceMethodEnd("ContentMigrationHelper.OnImportStarted");
        }

        /// <summary>
        /// Replaces the Url accordingly with the specified Url replacements rules.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="eventArgs"></param>
        private void OnImported(object sender, SPObjectImportedEventArgs eventArgs)
        {   
            this.Trace.TraceMethodEnd("ContentMigrationHelper.OnImported");

            if (!this.makeInternalLinksRelative)
                return;

            SPSite site = null;
            SPWeb web = null;
            SPListItem li = null;
            SPImport import = sender as SPImport;
            if (eventArgs.Type == SPDeploymentObjectType.ListItem)
            {
                if (!eventArgs.SourceUrl.ToLower().Contains("masterpage"))
                {
                    string url = GetSourceWebName(eventArgs.TargetUrl);
                    string listItemUrl = eventArgs.TargetUrl.Replace(url, "");
                    site = new SPSite(import.Settings.SiteUrl);
                    web = site.OpenWeb(url);
                    li = web.GetListItem(eventArgs.TargetUrl);
                    
                    if (li != null)
                    {
                        try
                        {
                            // for some reasons "li.ForwardLinks.Count" throws 
                            // an exception, even when ForwardLinks is null is not 
                            // empty
                            int count = li.ForwardLinks.Count;
                            for (int i = count - 1; i >= 0; i--)
                            {
                                // Get the old url
                                SPLink link = li.ForwardLinks[i];

                                if (link.IsInternal)
                                {
                                    string oldUrl = link.Url;

                                    // Replace the full Url
                                    this.Trace.TraceVerbose(Logging.UrlReplacing, link.Url, link.ServerRelativeUrl);
                                    li.ReplaceLink(link.Url, link.ServerRelativeUrl);
                                }
                            }
                        }
                        catch 
                        {
                            Trace.TraceWarning("SPListItem.ForwardLinkds.Count through an exception for this item ''", li.Url); 
                        }                       
                    }    
                }
            }

            if (web != null)
                web.Dispose();

            if (site != null)
                site.Dispose();
        }

        /// <summary>
        /// Re-parents all list items to point to the destination list
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        private void OnWebImportStarted(object sender, SPDeploymentEventArgs args)
        {
            this.Trace.TraceMethodStart("ContentMigrationHelper.OnWebImportStarted");
            
            // If there is no destination list associated with this object then we need to throw an exception because we cannot procceed
            // because there is no way to select the object that we want to import. All objects on the exported file have to be imported
            // We need to make sure that all lists on the content file map to a corrrespondent destination  list.
            ValidationHelper.VerifyObjectArgument(this.importWeb, "importWeb");

            SPWebHelper hlpr = new SPWebHelper();

            SPImport import = (SPImport)sender;

            SPImportObjectCollection rootObjects = args.RootObjects;
            foreach (SPImportObject importObject in rootObjects)
            {
                if (importObject.Type == SPDeploymentObjectType.Web)
                {
                    if (this.deleteDestinationWeb)
                    {
                        string newDestSite = importObject.TargetParentUrl + importObject.SourceUrl;
                        newDestSite = newDestSite.Replace(@"//", "/");

                        using (SPSite site = new SPSite(import.Settings.SiteUrl))
                        {
                            using (SPWeb webToDelete = site.OpenWeb(newDestSite))
                            {
                                SPWebHelper.DeleteWeb(webToDelete, true);
                            }
                        }
                    }
                }
                // Reparent the list item
                importObject.TargetParentUrl = this.importWeb.Url;                
            }

            this.Trace.TraceMethodEnd("ContentMigrationHelper.OnWebImportStarted");
        }

        #endregion
    }
}